/**
 * @DispatcherBiz.java  COPYRIGHT xj
 * 
 * Created on 2017年7月5日 下午4:43:32
 */
package xj.toolkit.access.dispatcher;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import xj.toolkit.bean.CommonsHeader;
import xj.toolkit.fsm.FSMContext;
import xj.toolkit.fsm.FiniteStateMachine;
import xj.toolkit.fsm.event.UUIDTimeoutEvent;
import xj.toolkit.fsm.tmpl.annotation.OnAccept;
import xj.toolkit.fsm.tmpl.annotation.OnEnter;
import xj.toolkit.fsm.tmpl.annotation.OnTimeout;
import xj.toolkit.fsm.tmpl.annotation.StateTemplate;
import xj.toolkit.netty.transport.TransportUtils;
import xj.toolkit.access.msg.AccessContentMessage;
import xj.toolkit.access.msg.AccessMessage;
import xj.toolkit.base.Sender;

/**
 * 接收客户端过来的数据，发送到远端。
 * 接收远端来的数据，发回给客户端。
 * 
 * @author <a href="mailto:cxj2000@gmail.com">xj.cao</a>
 *
 */
public class DispatcherBiz {

	private static final Logger logger = LoggerFactory
			.getLogger(DispatcherBiz.class);
	
	protected int timeout;
	
	protected String evenDispatch = "event.dispatch";
	
	public static final String FSM_CONTEXT_REQ = "REQUEST";
	public static final String FSM_CONTEXT_REP = "RESPONSE";
	
    @StateTemplate(init = true)
    class RecvReq {
        @OnAccept
        Class<?> accept(FiniteStateMachine fsm, FSMContext ctx, final AccessContentMessage req) {
            logger.debug("accept event: {}", req);
            setRequest(ctx, req);
            CommonsHeader commonsHeader = req.getCommonHeader();
            if (commonsHeader == null) {
            	logger.error("Receive invalid accessmessge{}, miss commonsheader.", req);
            	return null;
            }
            
            if (commonsHeader.getType() == CommonsHeader.RESPONSE) {
            	logger.error("Receive invalid accessmessge{}, miss commonsheader.", req);
            	return null;
            }
            
           return DispatchReq.class; 
        }
    }
    
    @StateTemplate
    class DispatchReq {
    	@OnEnter
		boolean enter(FiniteStateMachine fsm, FSMContext ctx) {
    		AccessMessage request = getRequest(ctx);
    		
    		UUIDTimeoutEvent tm = new UUIDTimeoutEvent(
					request.getIdentification());
			ctx.fireEventWithTimeout(tm, timeout, evenDispatch, request);
			
			CommonsHeader header = request.getCommonHeader();
			
			// -- 如果是通知，不必回应。
			if (header.getType() == CommonsHeader.NOTIFY) {
				return false;
			}
			
			return true;
    	}
    	
    	@OnTimeout
		void timeout(FiniteStateMachine fsm, FSMContext ctx) {
    		AccessMessage request = getRequest(ctx);
    		
    		logger.error("timeout[{}].", request);
    	}
    	
    	/**
		 * 收到来自后端的回应。
		 * 
		 * @param fsm 状态机
		 * @param ctx
		 * @param resp
		 * @return
		 */
		@OnAccept
		Class<?> accept(FiniteStateMachine fsm, FSMContext ctx,
				final AccessMessage resp)  {
			setResponse(ctx, resp);
			return SendTcpResp.class;
		}
    }
    
	@StateTemplate
	public class SendTcpResp {
		@OnEnter
		boolean enter(FiniteStateMachine fsm, FSMContext ctx) {
			// 处理向client返回
			if (getResponse(ctx) != null) {
				AccessMessage request = getRequest(ctx);
				AccessMessage response = getResponse(ctx);
				sendTcpResponse(request, response);
				if (ctx.getEndReason() == null) {
					ctx.setEndReason(request.getClass().getSimpleName());
				}
				return false;
			} else {
				logger.error("getResponse error!");
				return false;
			}
		}
	}
    
    // -- util method
	/**
	 * 发送tcp类型的回应。
	 * 
	 * @param request
	 * @param response
	 */
	protected void sendTcpResponse(AccessMessage request, AccessMessage response) {
		// send response
		Sender sender = TransportUtils.getSenderOf(request);
		if (logger.isDebugEnabled()) {
			logger.debug("SendClientRespState={}", response);
		}
		if (sender != null) {
			sender.send(response);
		} else {
			logger.error("getSender error!");
		}
	}
	
	@SuppressWarnings("unchecked")
	protected AccessMessage getRequest(FSMContext ctx) {
		return (AccessMessage) ctx.getProperty(FSM_CONTEXT_REQ);
	}

	protected void setRequest(FSMContext ctx, AccessMessage request) {
		ctx.setProperty(FSM_CONTEXT_REQ, request);
	}

	@SuppressWarnings("unchecked")
	protected AccessMessage getResponse(FSMContext ctx) {
		return (AccessMessage) ctx.getProperty(FSM_CONTEXT_REP);
	}

	protected void setResponse(FSMContext ctx, AccessMessage response) {
		ctx.setProperty(FSM_CONTEXT_REP, response);
	}

	public int getTimeout() {
		return timeout;
	}

	public void setTimeout(int timeout) {
		this.timeout = timeout;
	}

}
